#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<assert.h>

//画像のピクセル値をとっておくための構造体。
typedef struct colorImagePix{
	int width;
	int height;
	int* pixR;
	int* pixG;
	int* pixB;
}* ColorImagePix;

ColorImagePix mallocColorImage(width,height){
	ColorImagePix ret=(ColorImagePix)malloc(sizeof(struct colorImagePix)); assert(ret!=NULL);
	ret->width=width;
	ret->height=height;
	ret->pixR=(int*)malloc(sizeof(int)*width*height); assert(ret->pixR!=NULL);
	ret->pixG=(int*)malloc(sizeof(int)*width*height); assert(ret->pixG!=NULL);
	ret->pixB=(int*)malloc(sizeof(int)*width*height); assert(ret->pixB!=NULL);
	return ret;
}
void freeColorImage(ColorImagePix im){
	free(im->pixR);
	free(im->pixG);
	free(im->pixB);
	free(im);
}

typedef struct labelCount{
    int r;
    int g;
    int b;
    int count;
    struct labelCount* next;
}* LabelCount;

LabelCount calcImageLabel(ColorImagePix img);
void calcImageLabel_calc2(ColorImagePix img,int* label,int i,int j,int currentLabel,LabelCount cnt);
void calcImageLabel_calc3(ColorImagePix img,int* label,int i,int j,int currentLabel,LabelCount cnt);

LabelCount calcImageLabel(ColorImagePix img){
    int i,j;
    int nextlabel=1;
    LabelCount cnt=NULL;

    int* label=(int*)malloc(sizeof(int)*img->width*img->height); assert(label!=NULL);
    memset(label,0,sizeof(int)*img->width*img->height);
    for(i=0;i<img->height;i++){
        int _hOffset=i*img->width;
        for(j=0;j<img->width;j++){
            int _offset=_hOffset+j;
            if(label[_offset]==0){
                nextlabel++;

                LabelCount newCnt=(LabelCount)malloc(sizeof(struct labelCount)); assert(newCnt!=NULL);
                newCnt->r=img->pixR[_offset];
                newCnt->g=img->pixG[_offset];
                newCnt->b=img->pixB[_offset];
                newCnt->count=0;
                newCnt->next=cnt;
                cnt=newCnt;
                
                label[_offset]=nextlabel;
                calcImageLabel_calc2(img,label,i,j,nextlabel,newCnt);
            }
        }
    }
    
    free(label);
    return cnt;
}

void calcImageLabel_calc2(ColorImagePix img,int* label,int i,int j,int currentLabel,LabelCount cnt){
    if(i<0 || i>=img->height) return;
    if(j<0 || j>=img->width) return;
    
    cnt->count++;
    calcImageLabel_calc3(img,label,i-1,j+1,currentLabel,cnt);
    calcImageLabel_calc3(img,label,i,j+1,currentLabel,cnt);
    calcImageLabel_calc3(img,label,i+1,j+1,currentLabel,cnt);
    calcImageLabel_calc3(img,label,i+1,j,currentLabel,cnt);
    calcImageLabel_calc3(img,label,i+1,j-1,currentLabel,cnt);
}

//label[i,j]が設定されていなくてimg[i,j]の色がcnt->colorと等しければ、label[i,j]をcurrentLabel設定する
void calcImageLabel_calc3(ColorImagePix img,int* label,int i,int j,int currentLabel,LabelCount cnt){
    if(i<0 || i>=img->height) return;
    if(j<0 || j>=img->width) return;
	
    int _offset=i*img->width+j;

    if(label[_offset]==0 && cnt->r==img->pixR[_offset] && cnt->g==img->pixG[_offset] && cnt->b==img->pixB[_offset]){
        label[_offset]=currentLabel;
        calcImageLabel_calc2(img,label,i,j,currentLabel,cnt);
    }
}



int* calcCCV(ColorImagePix img,int th){
    int i,j,k,l;
    
    //ここまででガウシアンフィルタ適用されているとする
    
    //ReduceColor
    int colors[3*16]={
        0,0,0//black
        ,128,128,128//grey
        ,192,192,192//silver
        ,255,255,255//white
        ,255,0,0//red
        ,255,255,0//yellow
        ,0,255,0//lime
        ,0,255,255//aqua
        ,0,0,255//blue
        ,255,0,255//fuchsia
        ,128,0,0//maroon
        ,128,128,0//olive
        ,0,128,0//green
        ,0,128,128//teal
        ,0,0,128//navy
        ,128,0,128//purp
    };
    for(i=0;i<img->height;i++){
        int _hoffset=i*img->width;
        for(j=0;j<img->width;j++){
            int _offset=_hoffset+j;
            int pix[3]={img->pixR[_offset],img->pixG[_offset],img->pixB[_offset]};
            //detect nearest color
            int bestColor=-1;
            int bestSum=0;
            for(k=0;k<16;k++){
                int _color[3];
                memcpy(_color,&colors[3*k],sizeof(int)*3);
                int _sum=0;
                for(l=0;l<3;l++) {_color[l]-=pix[l];_color[l]*=_color[l]; _sum+=_color[l];}
                if(bestColor<0 ||_sum< bestSum){bestSum=_sum; bestColor=k;}
            }
            
            img->pixR[_offset]=colors[3*bestColor];
            img->pixG[_offset]=colors[3*bestColor+1];
            img->pixB[_offset]=colors[3*bestColor+2];
        }
    }
    LabelCount cnt=calcImageLabel(img);
    
    int* ret=(int*)malloc(sizeof(int)*16*2); assert(ret!=NULL);
    memset(ret,0,sizeof(int)*16*2);
    while(cnt!=NULL){
        for(k=0;k<16;k++){
            if(cnt->r==colors[3*k] && cnt->g==colors[3*k+1] && cnt->b==colors[3*k+2]){
                if(cnt->count>=th) ret[2*k]+=cnt->count;
                else ret[2*k+1]+=cnt->count;
                break;
            }
        }
        LabelCount p=cnt;
        cnt=cnt->next;
        free(p);
    }
    return ret;
}

void freeCCV(int* ccvResult){
    free(ccvResult);
}